package com.dlc.shop.api.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.tea.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.huifu.bspay.sdk.opps.core.request.V2TradePaymentJspayRequest;
import com.huifu.bspay.sdk.opps.core.utils.DateTools;
import com.huifu.bspay.sdk.opps.core.utils.SequenceTools;
import com.dlc.shop.api.huifu.OppsMerchantConfigDemo;
import com.dlc.shop.bean.app.param.PayParam;
import com.dlc.shop.bean.bo.PayInfoResultBO;
import com.dlc.shop.bean.model.*;
import com.dlc.shop.bean.pay.PayInfoDto;
import com.dlc.shop.common.exception.YamiShopBindException;
import com.dlc.shop.common.response.ResponseEnum;
import com.dlc.shop.common.response.ServerResponseEntity;
import com.dlc.shop.config.ShopConfig;
import com.dlc.shop.huifu.DemoTestConstants;
import com.dlc.shop.security.common.bo.LoginInfoBO;
import com.dlc.shop.security.common.manager.TokenStore;
import com.dlc.shop.service.*;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import static com.dlc.shop.api.huifu.BaseCommonDemo.doExecute;
import static com.dlc.shop.api.huifu.BaseCommonDemo.doInit;

/**
 * 汇付天下 微信支付
 */
@RestController
@RequestMapping("/huifu/wx")
@AllArgsConstructor
@Slf4j
public class HuifuWxController {

    private final OrderService orderService;
    private final ShopDetailService shopDetailService;
    private final SkuService skuService;
    private final OrderItemService orderItemService;
    private final TokenStore tokenStore;
    private final PayInfoService payInfoService;
    private final ShopConfig shopConfig;

    @PostMapping("/getOpenidByCode")
    @Transactional(rollbackFor = Exception.class)
    public ServerResponseEntity callback(@RequestBody PayParam payParam) throws Exception{

        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                "appid=" + shopConfig.getWxMp().getAppId() +
                "&secret=" + shopConfig.getWxMp().getSecret() +
                "&code=" + payParam.getCode() +
                "&grant_type=authorization_code";
//         发送请求，并接收响应
//         获取后完成接下来的业务 如果需要向前端传递openid参数可以将openid放入缓存，重定向前端后前端再查询获取，或者直接携带为url的query参数传递给前端

            HttpRequest httpRequest = HttpRequest.post(url).header("Content-Type", "application/json"); // 设置请求头
            HttpResponse response = httpRequest.execute();
            String body = response.body();
            JSONObject res = JSONObject.parseObject(body);
            String openId = res.getString("openid");
//            String openId = "o8jhot8zMfAh9Bgc9nv394A-6GQg";
            if(openId != null && !openId.equals(""))
            {
                List<Order> orderList = orderService.list(new LambdaQueryWrapper<Order>()
                        .in(Order::getOrderNumber, payParam.getOrderNumbers().split(StrUtil.COMMA)));
                if(orderList.size() <= 0) {
                    return ServerResponseEntity.fail(ResponseEnum.SHOW_FAIL,"订单信息不存在");
                }
                String userId = orderList.get(0).getUserId();
                String bizUserId = null;
                LoginInfoBO loginInfo = tokenStore.getLoginInfoByCache();
                if (loginInfo!=null) {
                    bizUserId = loginInfo.getBizUserId();
                }
                PayInfoDto payInfo = payInfoService.pay(userId, payParam,null, bizUserId);

                // 1. 数据初始化
                doInit(OppsMerchantConfigDemo.getMerchantConfig());

                // 2.组装请求参数
                V2TradePaymentJspayRequest request = new V2TradePaymentJspayRequest();
                // 请求日期
                request.setReqDate(DateTools.getCurrentDateYYYYMMDD());
                // 请求流水号
                request.setReqSeqId(SequenceTools.getReqSeqId32());
                // 商户号
                request.setHuifuId(DemoTestConstants.DEMO_SYS_ID);
                // 商品描述
                request.setGoodsDesc(orderList.stream().map(Order::getProdName).collect(Collectors.joining(",")));
                // 交易类型
                request.setTradeType(payParam.getTradeType());
                // 交易金额
                request.setTransAmt(String.format("%.2f",  orderList.stream().mapToDouble(Order::getActualTotal).sum()));

                // 设置非必填字段
                Map<String, Object> extendInfoMap = getExtendInfos(orderList);
                Map<String, String> wxData = new HashMap<>();
                wxData.put("sub_appid",shopConfig.getWxMp().getAppId());
                wxData.put("openid",openId);
                wxData.put("sub_openid",openId);
                wxData.put("attach",payInfo.getPayNo());

                extendInfoMap.put("wx_data", com.alibaba.fastjson.JSONObject.toJSONString(wxData));
                request.setExtendInfo(extendInfoMap);

                // 3. 发起API调用
                Map<String, Object> responseRes = doExecute(request);
                log.info("返回数据:" + com.alibaba.fastjson.JSONObject.toJSONString(responseRes));
                try {
                    if(!responseRes.get("trans_stat").equals("F")){
                        QueryWrapper<PayInfo> queryWrapper = new QueryWrapper();
                        queryWrapper.lambda().eq(PayInfo::getPayNo, payInfo.getPayNo());
                        PayInfo finalPayInfo = payInfoService.getOne(queryWrapper);
                        finalPayInfo.setBizPayNo(request.getReqSeqId());
                        finalPayInfo.setReqDate(request.getReqDate());
                        finalPayInfo.setPayInfo(responseRes.get("pay_info").toString());
                        payInfoService.update(finalPayInfo, new LambdaUpdateWrapper<PayInfo>().eq(PayInfo::getPayNo, finalPayInfo.getPayNo()));

                        return ServerResponseEntity.success(responseRes.get("pay_info"));
                    }else {
                        throw new RuntimeException(responseRes.get("resp_desc").toString());
                    }
                } catch (RuntimeException e) {
                    return ServerResponseEntity.fail(ResponseEnum.SHOW_FAIL,e.getMessage());
                }
            }
            return ServerResponseEntity.fail(ResponseEnum.SHOW_FAIL,"获取微信授权失败");
    }

    /**
     * 微信小程序支付
     * @param payParam
     * @return
     * @throws Exception
     */
    @PostMapping("/pay")
    @Transactional(rollbackFor = Exception.class)
    public ServerResponseEntity pay(@RequestBody PayParam payParam) throws Exception{


        LoginInfoBO loginInfo = tokenStore.getLoginInfoByCache();
        String openId = loginInfo.getBizUserId();

        if(StringUtils.isEmpty(openId)){
            String url = "https://api.weixin.qq.com/sns/jscode2session?" +
                    "appid=" + shopConfig.getWxMiniApp().getAppId() +
                    "&secret=" + shopConfig.getWxMiniApp().getSecret() +
                    "&js_code=" + payParam.getCode() +
                    "&grant_type=authorization_code";
//         发送请求，并接收响应
//         获取后完成接下来的业务 如果需要向前端传递openid参数可以将openid放入缓存，重定向前端后前端再查询获取，或者直接携带为url的query参数传递给前端

            HttpRequest httpRequest = HttpRequest.post(url).header("Content-Type", "application/json"); // 设置请求头
            HttpResponse response = httpRequest.execute();
            String body = response.body();
            JSONObject res = JSONObject.parseObject(body);
            openId = res.getString("openid");
        }

        if(openId != null && !openId.equals(""))
        {
            List<Order> orderList = orderService.list(new LambdaQueryWrapper<Order>()
                    .in(Order::getOrderNumber, payParam.getOrderNumbers().split(StrUtil.COMMA)));
            if(orderList.size() <= 0) {
                return ServerResponseEntity.fail(ResponseEnum.SHOW_FAIL,"订单信息不存在");
            }
            String userId = orderList.get(0).getUserId();
            String bizUserId = null;
            //LoginInfoBO loginInfo = tokenStore.getLoginInfoByCache();
            if (loginInfo!=null) {
                bizUserId = loginInfo.getBizUserId();
            }
            PayInfoDto payInfo = payInfoService.pay(userId, payParam,null, bizUserId);

            // 1. 数据初始化
            doInit(OppsMerchantConfigDemo.getMerchantConfig());

            // 2.组装请求参数
            V2TradePaymentJspayRequest request = new V2TradePaymentJspayRequest();
            // 请求日期
            request.setReqDate(DateTools.getCurrentDateYYYYMMDD());
            // 请求流水号
            request.setReqSeqId(SequenceTools.getReqSeqId32());
            // 商户号
            request.setHuifuId(DemoTestConstants.DEMO_SYS_ID);
            // 商品描述
            request.setGoodsDesc(orderList.stream().map(Order::getProdName).collect(Collectors.joining(",")));
            // 交易类型
            request.setTradeType(payParam.getTradeType());
            // 交易金额
            request.setTransAmt(String.format("%.2f",  orderList.stream().mapToDouble(Order::getActualTotal).sum()));

            // 设置非必填字段
            Map<String, Object> extendInfoMap = getExtendInfos(orderList);
            Map<String, String> wxData = new HashMap<>();
            wxData.put("sub_appid",shopConfig.getWxMiniApp().getAppId());
            wxData.put("openid",openId);
            wxData.put("sub_openid",openId);
            wxData.put("attach",payInfo.getPayNo());

            extendInfoMap.put("wx_data", com.alibaba.fastjson.JSONObject.toJSONString(wxData));
            request.setExtendInfo(extendInfoMap);

            // 3. 发起API调用
            Map<String, Object> responseRes = doExecute(request);
            log.info("返回数据:" + com.alibaba.fastjson.JSONObject.toJSONString(responseRes));
            try {
                if(!responseRes.get("trans_stat").equals("F")){
                    QueryWrapper<PayInfo> queryWrapper = new QueryWrapper();
                    queryWrapper.lambda().eq(PayInfo::getPayNo, payInfo.getPayNo());
                    PayInfo finalPayInfo = payInfoService.getOne(queryWrapper);
                    finalPayInfo.setBizPayNo(request.getReqSeqId());
                    finalPayInfo.setReqDate(request.getReqDate());
                    finalPayInfo.setPayInfo(responseRes.get("pay_info").toString());
                    payInfoService.update(finalPayInfo, new LambdaUpdateWrapper<PayInfo>().eq(PayInfo::getPayNo, finalPayInfo.getPayNo()));

                    return ServerResponseEntity.success(responseRes.get("pay_info"));
                }else {
                    throw new RuntimeException(responseRes.get("resp_desc").toString());
                }
            } catch (RuntimeException e) {
                return ServerResponseEntity.fail(ResponseEnum.SHOW_FAIL,e.getMessage());
            }
        }
        return ServerResponseEntity.fail(ResponseEnum.SHOW_FAIL,"获取微信授权失败");
    }

    private  Map<String, Object> getExtendInfos(List<Order> orderList) throws ParseException {
        // 设置非必填字段
        Map<String, Object> extendInfoMap = new HashMap<>();
        // 账户号
        // extendInfoMap.put("acct_id", "");
        // 交易有效期
        LocalDateTime localDateTime = LocalDateTime.now().plusMinutes(30);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
        String format = sdf.format(date);
        extendInfoMap.put("time_expire", format);
        // 是否延迟交易
        extendInfoMap.put("delay_acct_flag", "Y");
        // 异步通知地址
        extendInfoMap.put("notify_url", shopConfig.getDomain().getApiDomainName()+"/huifu/wx/huiFuCallback");
//        // 微信参数集合
//        // extendInfoMap.put("wx_data", getWxData());
//        // 支付宝参数集合
////        extendInfoMap.put("alipay_data", getAlipayData());
//        // 银联参数集合
//        // extendInfoMap.put("unionpay_data", getUnionpayData());
//        // 手续费扣款标志
//        // extendInfoMap.put("fee_flag", "");
//        // 分账对象
        extendInfoMap.put("acct_split_bunch", getAcctSplitBunch(orderList));
//        // 传入分账遇到优惠的处理规则
//        extendInfoMap.put("term_div_coupon_type", "0");
//        // 补贴支付信息
//        // extendInfoMap.put("combinedpay_data", getCombinedpayData());
//        // 禁用信用卡标记
//        extendInfoMap.put("limit_pay_type", "NO_CREDIT");
//        // 商户贴息标记
//        extendInfoMap.put("fq_mer_discount_flag", "N");
//        // 渠道号
//        extendInfoMap.put("channel_no", "");
//        // 场景类型
//        extendInfoMap.put("pay_scene", "02");
//        // 备注
//        extendInfoMap.put("remark", "String");
        // 安全信息
//        extendInfoMap.put("risk_check_data", getRiskCheckData());
        // 设备信息
//        extendInfoMap.put("terminal_device_data", getTerminalDeviceData());
        return extendInfoMap;
    }

    private  String getAcctSplitBunch(List<Order> orderList) {
        com.alibaba.fastjson.JSONObject dto = new com.alibaba.fastjson.JSONObject();
        // 分账明细
        dto.put("acct_infos", getAcctInfosRucan(orderList));

        return dto.toJSONString();
    }
    private  JSON getAcctInfosRucan(List<Order> orderList) {
        Double totalMoney = 0d;
        JSONArray dtoList = new JSONArray();
        for (Order order : orderList) {
            ShopDetail shopDetail = shopDetailService.getById(order.getShopId());
            if(Objects.isNull(shopDetail.getHuifuId())){
                //如果汇付 id 为null 的情况下告诉页面  拉起二维码失败
                throw  new YamiShopBindException("支付拉起失败,请联系平台客服！");
            }
             if(shopDetail.getHuifuId().equals(DemoTestConstants.DEMO_SYS_ID)) {
                continue;
            }
            double aDouble = 0d;
            List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(order.getOrderNumber(), false);
            for (OrderItem orderItem : orderItems) {
                Sku sku = skuService.getSkuBySkuId(orderItem.getSkuId());
                aDouble += (sku.getSupplierPrice() * orderItem.getProdCount());
            }

            JSONObject dto = new JSONObject();
            // 分账金额
            dto.put("div_amt", BigDecimal.valueOf(aDouble).setScale(2, RoundingMode.HALF_UP));
            // 分账接收方ID
            dto.put("huifu_id", shopDetail.getHuifuId());
            dtoList.add(dto);
            totalMoney += aDouble;
        }

        Double sumActualTotal = orderList.stream().mapToDouble(Order::getActualTotal).sum();


        JSONObject dto1 = new JSONObject();
        dto1.put("div_amt", BigDecimal.valueOf(sumActualTotal - totalMoney).setScale(2, RoundingMode.HALF_UP));
        // 分账接收方ID
        dto1.put("huifu_id", DemoTestConstants.DEMO_SYS_ID);
        dtoList.add(dto1);
        return dtoList;
    }

    @PostMapping("/huiFuCallback")
    @Operation(summary = "汇付回调")
    public String huiFuCallback(HttpServletRequest request){
            String respCode = request.getParameter("resp_code");
            JSONObject respData = JSONObject.parseObject(request.getParameter("resp_data"));
            log.info("汇付回调返回报文-------------------------："+respData.toString());
            PayInfo payInfo = payInfoService.getOne(new LambdaQueryWrapper<PayInfo>()
                    .eq(PayInfo::getBizPayNo, respData.get("req_seq_id").toString()));

            if(payInfo == null){
                return "";
            }

            if(respData.get("resp_code").equals("00000000") && respData.get("trans_stat").equals("S")){

                JSONObject jsonObject = respData.getJSONObject("wx_response");

                PayInfoResultBO payInfoResultBO = new PayInfoResultBO();
                payInfoResultBO.setPayNo(jsonObject.get("attach").toString());
                payInfoResultBO.setBizPayNo(respData.get("req_seq_id").toString());
                payInfoResultBO.setIsPaySuccess(true);
                payInfoResultBO.setPayAmount(Double.valueOf(respData.get("trans_amt").toString()));
                payInfoResultBO.setCallbackContent(respData.toString());

                payInfoService.noticeOrder(payInfoResultBO, payInfo,0);

                return "RECV_ORD_ID_" + respData.get("req_seq_id").toString();
            }
        return "";
    }
}

